jetcrab\vm\executor\instruction_handlers/
builtin_calls.rs

1//! # Builtin Calls Handler
2//!
3//! Handles execution of JavaScript built-in functions including console operations,
4//! type conversion functions, and utility functions. Provides implementations
5//! for commonly used JavaScript global functions.
6//!
7//! ## Supported Functions
8//!
9//! ### Console Functions
10//! - **console.log**: Output values to stdout
11//! - **console.error**: Output values to stderr
12//!
13//! ### Type Conversion
14//! - **parseInt**: Parse string to integer
15//! - **parseFloat**: Parse string to floating point
16//! - **isNaN**: Check if value is NaN
17//! - **isFinite**: Check if value is finite
18//!
19//! ### String Functions
20//! - **encodeURI**: Encode URI string (simplified)
21//! - **decodeURI**: Decode URI string (simplified)
22//! - **escape**: Escape string for URL (simplified)
23//! - **unescape**: Unescape URL string (simplified)
24//!
25//! ## Implementation Notes
26//!
27//! The builtin functions are implemented with simplified behavior compared
28//! to full JavaScript semantics, focusing on common use cases and basic
29//! functionality expected in a VM environment.
30//!
31//! ## Usage
32//!
33//! ```rust
34//! use jetcrab::vm::executor::instruction_handlers::BuiltinCallsHandler;
35//!
36//! BuiltinCallsHandler::call_console_log(&mut stack, &mut variables, 2)?;
37//! BuiltinCallsHandler::call_parse_int(&mut stack, &mut variables, 1)?;
38//! ```
39
40use crate::runtime::builtins::Builtins;
41use crate::vm::executor::error_handler::ExecutionError;
42use crate::vm::executor::traits::{StackOperations, VariableManager};
43use crate::vm::value::Value;
44
45/// Handles execution of JavaScript built-in functions
46///
47/// Provides implementations for common JavaScript global functions
48/// including console operations, type conversions, and utilities.
49pub struct BuiltinCallsHandler;
50
51impl BuiltinCallsHandler {
52    pub fn call_builtin<S, V>(
53        stack: &mut S,
54        _variable_manager: &mut V,
55        builtins: &mut Builtins,
56        function_name: String,
57        arg_count: usize,
58    ) -> Result<(), ExecutionError>
59    where
60        S: StackOperations,
61        V: VariableManager,
62    {
63        let mut args = Vec::new();
64        for _ in 0..arg_count {
65            args.push(stack.pop().ok_or(ExecutionError::StackUnderflow)?);
66        }
67        args.reverse();
68
69        let result = match builtins.get_function(&function_name) {
70            Some(_) => Value::String(format!("builtin:{}", function_name)),
71            None => Value::Undefined,
72        };
73        stack.push(result);
74        Ok(())
75    }
76
77    pub fn call_console_log<S, V>(
78        stack: &mut S,
79        _variable_manager: &mut V,
80        arg_count: usize,
81    ) -> Result<(), ExecutionError>
82    where
83        S: StackOperations,
84        V: VariableManager,
85    {
86        let mut args = Vec::new();
87        for _ in 0..arg_count {
88            args.push(stack.pop().ok_or(ExecutionError::StackUnderflow)?);
89        }
90
91        args.reverse();
92
93        print!("console.log: ");
94        for (i, arg) in args.iter().enumerate() {
95            if i > 0 {
96                print!(" ");
97            }
98            print!("{:?}", arg);
99        }
100        println!();
101
102        stack.push(Value::Undefined);
103        Ok(())
104    }
105
106    pub fn call_console_error<S, V>(
107        stack: &mut S,
108        _variable_manager: &mut V,
109        arg_count: usize,
110    ) -> Result<(), ExecutionError>
111    where
112        S: StackOperations,
113        V: VariableManager,
114    {
115        let mut args = Vec::new();
116        for _ in 0..arg_count {
117            args.push(stack.pop().ok_or(ExecutionError::StackUnderflow)?);
118        }
119
120        args.reverse();
121
122        eprint!("console.error: ");
123        for (i, arg) in args.iter().enumerate() {
124            if i > 0 {
125                eprint!(" ");
126            }
127            eprint!("{:?}", arg);
128        }
129        eprintln!();
130
131        stack.push(Value::Undefined);
132        Ok(())
133    }
134
135    pub fn call_parse_int<S, V>(
136        stack: &mut S,
137        _variable_manager: &mut V,
138        arg_count: usize,
139    ) -> Result<(), ExecutionError>
140    where
141        S: StackOperations,
142        V: VariableManager,
143    {
144        let mut args = Vec::new();
145        for _ in 0..arg_count {
146            args.push(stack.pop().ok_or(ExecutionError::StackUnderflow)?);
147        }
148        args.reverse();
149
150        let result = if let Some(Value::String(s)) = args.get(0) {
151            let radix = args
152                .get(1)
153                .and_then(|v| {
154                    if let Value::Number(n) = v {
155                        Some(*n as i32)
156                    } else {
157                        None
158                    }
159                })
160                .unwrap_or(10);
161
162            match i64::from_str_radix(s, radix as u32) {
163                Ok(n) => Value::Number(n as f64),
164                Err(_) => Value::Number(f64::NAN),
165            }
166        } else {
167            Value::Number(f64::NAN)
168        };
169
170        stack.push(result);
171        Ok(())
172    }
173
174    pub fn call_parse_float<S, V>(
175        stack: &mut S,
176        _variable_manager: &mut V,
177        arg_count: usize,
178    ) -> Result<(), ExecutionError>
179    where
180        S: StackOperations,
181        V: VariableManager,
182    {
183        let mut args = Vec::new();
184        for _ in 0..arg_count {
185            args.push(stack.pop().ok_or(ExecutionError::StackUnderflow)?);
186        }
187        args.reverse();
188
189        let result = if let Some(Value::String(s)) = args.get(0) {
190            match s.parse::<f64>() {
191                Ok(n) => Value::Number(n),
192                Err(_) => Value::Number(f64::NAN),
193            }
194        } else {
195            Value::Number(f64::NAN)
196        };
197
198        stack.push(result);
199        Ok(())
200    }
201
202    pub fn call_is_nan<S, V>(
203        stack: &mut S,
204        _variable_manager: &mut V,
205        arg_count: usize,
206    ) -> Result<(), ExecutionError>
207    where
208        S: StackOperations,
209        V: VariableManager,
210    {
211        let mut args = Vec::new();
212        for _ in 0..arg_count {
213            args.push(stack.pop().ok_or(ExecutionError::StackUnderflow)?);
214        }
215        args.reverse();
216
217        let result = if let Some(Value::Number(n)) = args.get(0) {
218            Value::Boolean(n.is_nan())
219        } else {
220            Value::Boolean(true)
221        };
222
223        stack.push(result);
224        Ok(())
225    }
226
227    pub fn call_is_finite<S, V>(
228        stack: &mut S,
229        _variable_manager: &mut V,
230        arg_count: usize,
231    ) -> Result<(), ExecutionError>
232    where
233        S: StackOperations,
234        V: VariableManager,
235    {
236        let mut args = Vec::new();
237        for _ in 0..arg_count {
238            args.push(stack.pop().ok_or(ExecutionError::StackUnderflow)?);
239        }
240        args.reverse();
241
242        let result = if let Some(Value::Number(n)) = args.get(0) {
243            Value::Boolean(n.is_finite())
244        } else {
245            Value::Boolean(false)
246        };
247
248        stack.push(result);
249        Ok(())
250    }
251
252    pub fn call_encode_uri<S, V>(
253        stack: &mut S,
254        _variable_manager: &mut V,
255        arg_count: usize,
256    ) -> Result<(), ExecutionError>
257    where
258        S: StackOperations,
259        V: VariableManager,
260    {
261        let mut args = Vec::new();
262        for _ in 0..arg_count {
263            args.push(stack.pop().ok_or(ExecutionError::StackUnderflow)?);
264        }
265        args.reverse();
266
267        let result = if let Some(Value::String(s)) = args.get(0) {
268            let encoded = s
269                .chars()
270                .map(|c| {
271                    if c.is_alphanumeric() || "!*'();:@&=+$,/?#[]".contains(c) {
272                        c.to_string()
273                    } else {
274                        format!("%{:02X}", c as u32)
275                    }
276                })
277                .collect::<String>();
278            Value::String(encoded)
279        } else {
280            Value::String("".to_string())
281        };
282
283        stack.push(result);
284        Ok(())
285    }
286
287    pub fn call_decode_uri<S, V>(
288        stack: &mut S,
289        _variable_manager: &mut V,
290        arg_count: usize,
291    ) -> Result<(), ExecutionError>
292    where
293        S: StackOperations,
294        V: VariableManager,
295    {
296        let mut args = Vec::new();
297        for _ in 0..arg_count {
298            args.push(stack.pop().ok_or(ExecutionError::StackUnderflow)?);
299        }
300        args.reverse();
301
302        let result = if let Some(Value::String(s)) = args.get(0) {
303            let decoded = s
304                .replace("%20", " ")
305                .replace("%2F", "/")
306                .replace("%3F", "?")
307                .replace("%23", "#");
308            Value::String(decoded)
309        } else {
310            Value::String("".to_string())
311        };
312
313        stack.push(result);
314        Ok(())
315    }
316
317    pub fn call_escape<S, V>(
318        stack: &mut S,
319        _variable_manager: &mut V,
320        arg_count: usize,
321    ) -> Result<(), ExecutionError>
322    where
323        S: StackOperations,
324        V: VariableManager,
325    {
326        let mut args = Vec::new();
327        for _ in 0..arg_count {
328            args.push(stack.pop().ok_or(ExecutionError::StackUnderflow)?);
329        }
330        args.reverse();
331
332        let result = if let Some(Value::String(s)) = args.get(0) {
333            let escaped = s
334                .chars()
335                .map(|c| match c {
336                    'A'..='Z' | 'a'..='z' | '0'..='9' | '@' | '*' | '_' | '+' | '-' | '.' | '/' => {
337                        c.to_string()
338                    }
339                    _ => format!("%{:02X}", c as u32),
340                })
341                .collect::<String>();
342            Value::String(escaped)
343        } else {
344            Value::String("".to_string())
345        };
346
347        stack.push(result);
348        Ok(())
349    }
350
351    pub fn call_unescape<S, V>(
352        stack: &mut S,
353        _variable_manager: &mut V,
354        arg_count: usize,
355    ) -> Result<(), ExecutionError>
356    where
357        S: StackOperations,
358        V: VariableManager,
359    {
360        let mut args = Vec::new();
361        for _ in 0..arg_count {
362            args.push(stack.pop().ok_or(ExecutionError::StackUnderflow)?);
363        }
364        args.reverse();
365
366        let result = if let Some(Value::String(s)) = args.get(0) {
367            let mut unescaped = String::new();
368            let mut chars = s.chars().peekable();
369
370            while let Some(c) = chars.next() {
371                if c == '%' {
372                    if let (Some(h1), Some(h2)) = (chars.next(), chars.next()) {
373                        if let Ok(byte) = u8::from_str_radix(&format!("{}{}", h1, h2), 16) {
374                            unescaped.push(byte as char);
375                            continue;
376                        }
377                    }
378                    unescaped.push('%');
379                    if let Some(h1) = chars.next() {
380                        unescaped.push(h1);
381                    }
382                    if let Some(h2) = chars.next() {
383                        unescaped.push(h2);
384                    }
385                } else {
386                    unescaped.push(c);
387                }
388            }
389
390            Value::String(unescaped)
391        } else {
392            Value::String("".to_string())
393        };
394
395        stack.push(result);
396        Ok(())
397    }
398}